package org.hishenframework.core.data.jdbc.template;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.Column;
import javax.persistence.Table;
import javax.persistence.TableGenerator;
import javax.sql.DataSource;

import org.springframework.dao.support.DataAccessUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.ParameterizedBeanPropertyRowMapper;

public abstract class AbstractHishenJdbcTemplate<T> implements HishenJdbcTemplate<T> {
	
	/** 日志 */
//	private Logger logger = LoggerFactory.getLogger(AbstractHishenJdbcTemplate.class);
	//http://www.html.org.cn/books/springReference/ch11s05.html
	/** 数据源 */
	private DataSource dataSource;
	/** jdbc模板 */
	private JdbcTemplate jdbcTemplate = new JdbcTemplate();
	/** 序列 */
	private Map<String, TableSequence> tableSequenceMap = new HashMap<String, TableSequence>();
	/** 创建序列时的锁 */
	private Object createTableSequenceLock = new Object();
	/** 系统默认记录自增表名称 */
	public final static String GEN_TABLE = "t_sys_gen_table";
	public final static String PK_COLUMN_NAME = "pk_column";
	public final static String VALUE_COLUMN_NAME = "value_column";
	public final static int INITIAL_VALUE = 50;
	public final static int ALLOCATION_SIZE = 1;
	
	/**
	 * 获取新自增号
	 * @param field
	 * @return
	 * @throws SecurityException 
	 * @throws NoSuchFieldException 
	 */
	public long newGen(Class<?> entityClass, String fieldName) throws NoSuchFieldException, SecurityException {
		Field field = entityClass.getField(fieldName);
		return newGen(entityClass, field);
	}

	public long newGen(Class<?> entityClass, Field field) {
		String genTable = GEN_TABLE;
		String pkColumnName = PK_COLUMN_NAME;
		String valueColumnName = VALUE_COLUMN_NAME;
		String pkColumnValue = null;
		int initialValue = INITIAL_VALUE;
		int allocationSize = ALLOCATION_SIZE;
		
		TableGenerator tableGenerator = field.getAnnotation(TableGenerator.class);
		if (tableGenerator != null) {
			if (!tableGenerator.table().equals("")) {
				genTable = tableGenerator.table();
			}
			if (!tableGenerator.pkColumnName().equals("")) {
				pkColumnName = tableGenerator.pkColumnName();
			}
			if (!tableGenerator.valueColumnName().equals("")) {
				valueColumnName = tableGenerator.valueColumnName();
			}
			initialValue = tableGenerator.initialValue();
			allocationSize = tableGenerator.allocationSize();
		} 
		if (tableGenerator != null && !tableGenerator.pkColumnValue().equals("")){
			pkColumnValue = tableGenerator.pkColumnValue();
		} else {
			String begin = entityClass.getSimpleName();
			Table tableAnn = entityClass.getAnnotation(Table.class);
			if (tableAnn != null && !tableAnn.name().equals("")) {
				begin = tableAnn.name();
			}
			
			String end = field.getName();
			Column column = field.getAnnotation(Column.class);
			if (column != null && !column.name().equals("")) {
				end = column.name();
			}
			pkColumnValue = begin + "_c_" + end;
		}
		return newGen(genTable, pkColumnName, valueColumnName, pkColumnValue, initialValue, allocationSize);
	}
	
	public long newGen(String pkColumnValue, int initialValue, int allocationSize) {
		return newGen(GEN_TABLE, PK_COLUMN_NAME, VALUE_COLUMN_NAME, pkColumnValue, initialValue, allocationSize);
	}
	
	public long newGen(String genTable, String pkColumnName, String valueColumnName, String pkColumnValue, int initialValue, int allocationSize) {
		TableSequence sequence = null;
		sequence = tableSequenceMap.get(pkColumnValue);
		if (sequence == null) {
			synchronized (createTableSequenceLock) {
				if (sequence == null) {
					List<Map<String, Object>> result = queryForListMap("SELECT " + valueColumnName + " FROM " + genTable + " WHERE " + pkColumnName + " = ?", pkColumnValue);
					long seq = initialValue;
					if (result.size() == 0) {
						update("INSERT INTO " + genTable + "(" + pkColumnName + "," + valueColumnName + ") VALUES (?, ?)", pkColumnValue, initialValue);
					} else {
						seq = (long) result.get(0).get(valueColumnName);
					}
					sequence = new TableSequence(pkColumnValue, seq);
					tableSequenceMap.put(pkColumnValue, sequence);
				}
			}
		}
		synchronized (sequence) {
			update("UPDATE " + genTable + " SET " + VALUE_COLUMN_NAME + " = " + VALUE_COLUMN_NAME + " + ? WHERE " + pkColumnName + " = ?", allocationSize, pkColumnValue);
			sequence.add(allocationSize);
		}
		return sequence.getValue();
	}

	/**
	 * 执行insert update delete 等语句
	 * @param sql
	 * @param values
	 * @return
	 */
	@Override
	public int update(String sql, Object... values) {
		return jdbcTemplate.update(sql, values);
	}

	/**
	 * 获取int值
	 * @param sql
	 * @param values
	 * @return
	 */
	@Override
	public int queryForInt(String sql, Object... values) {
		Number number = jdbcTemplate.queryForObject(sql, values, Integer.class);
		return (number != null ? number.intValue() : 0);
	}

	/**
	 * 获取long值
	 * @param sql
	 * @param values
	 * @return
	 */
	@Override
	public long queryForLong(String sql, Object... values) {
		Number number = jdbcTemplate.queryForObject(sql, values, Long.class);
		return (number != null ? number.longValue() : 0);
	}
	
	/**
	 * 获取一行中一列的值
	 * @param sql
	 * @param values
	 * @return
	 */
	public Object queryForObject(String sql, Object... values) {
		return jdbcTemplate.queryForObject(sql, values, Object.class);
	}
	
	/**
	 * 获取一个实体
	 * @param sql
	 * @param entityClass
	 * @param values
	 * @return
	 */
	@Override
	public T queryForBean(String sql, Class<T> entityClass, Object... values) {
		RowMapper<T> rowMapper = ParameterizedBeanPropertyRowMapper.newInstance(entityClass);
		List<T> results = jdbcTemplate.query(sql, rowMapper, values);
		return DataAccessUtils.singleResult(results);
	}
	
	/**
	 * 获取一个实体
	 * @param sql
	 * @param values
	 * @return
	 */
	@Override
	public Map<String, Object> queryForMap(String sql, Object... values) {
		return jdbcTemplate.queryForMap(sql, values);
	}
	
	/**
	 * 获取实体集合
	 * @param sql
	 * @param entityClass
	 * @param values
	 * @return
	 */
	@Override
	public List<T> queryForListBean(String sql, Class<T> entityClass, Object... values) {		
		RowMapper<T> rowMapper = ParameterizedBeanPropertyRowMapper.newInstance(entityClass);
//		logger.debug(sql);
//		logger.debug("WHERE-参数 " + Arrays.toString(values));
		return jdbcTemplate.query(sql, rowMapper, values);
	}
	
	/**
	 * 获取实体集合
	 * @param sql
	 * @param values
	 * @return
	 */
	@Override
	public List<Map<String, Object>> queryForListMap(String sql, Object... values) {		
//		logger.debug(sql);
//		logger.debug("WHERE-参数 " + Arrays.toString(values));
		return jdbcTemplate.queryForList(sql, values);
	}
	
	/**
	 * 填充page
	 * @param dataPaging
	 * @param sql
	 * @param entityClass
	 * @param values
	 * @return
	 */
	@Override
	public void queryForDataPaging(DataPaging<T> dataPaging, String sql, Class<T> entityClass, Object... values) {
		long offset = dataPaging.getPageSize() * (dataPaging.getCurrentPage() - 1);
		long length = dataPaging.getPageSize();
		List<T> result = queryForListBean(offset, length, sql, entityClass,values);
		dataPaging.setResult(result);
		//获取对应的记录数
		long totalCount = queryForCount(sql, values);
		dataPaging.setTotalCount(totalCount);
	}
	
	@Override
	public void queryForDataPaging(DataPaging<Map<String, Object>> dataPaging, String sql, Object... values) {
		long offset = dataPaging.getPageSize() * (dataPaging.getCurrentPage() - 1);
		long length = dataPaging.getPageSize();
		List<Map<String, Object>> result = queryForListMap(offset, length, sql, values);
		//获取对应的记录数
		long totalCount = queryForCount(sql, values);
		dataPaging.setResult(result);
		dataPaging.setTotalCount(totalCount);
	}
	
	@Override
	public DataSource getDataSource() {
		return dataSource;
	}

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
		this.jdbcTemplate.setDataSource(dataSource);
	}

	public JdbcTemplate getJdbcTemplate() {
		return jdbcTemplate;
	}
}
